package com.ficus.road.maintain.service;

import com.aliyun.oss.model.OSSObject;
import com.ficus.road.maintain.core.model.CarVideoRecord;
import com.ficus.road.maintain.core.model.GpsLocation;
import com.ficus.road.maintain.core.model.ReplayVideoRequestDTO;
import com.ficus.road.maintain.core.model.ReplayVideoResponseDTO;
import com.ficus.road.maintain.dao.CarVideoRecordDao;
import com.ficus.road.maintain.process.LoadSsfFileProcessor;
import com.ficus.road.maintain.util.AliyunOSSUtil;
import ficus.car_demo.FrameMeta;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.aggregation.MatchOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

import static com.ficus.road.maintain.util.AliyunOSSUtil.byteArrayToInt;

@Service
@Slf4j
public class VideoService {
    public static volatile DateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    public static List<List<ReplayVideoResponseDTO>> historyVideos = new ArrayList<>();
    @Getter
    private LoadSsfFileProcessor loadSsfFileProcessor;
    @Autowired
    private AliyunOSSUtil aliyunOSSUtil;
    @Autowired
    private DataProcessorManager dataProcessorManager;
    @Autowired
    private Executor taskAsyncPool;
    @Autowired
    private GpsHistoryService gpsHistoryService;

    @Autowired
    private CarVideoRecordService carVideoRecordService;

    @Autowired
    private CarVideoRecordDao carVideoRecordDao;

    @Autowired
    private HitService hitService;

    @PostConstruct
    void init() {
        loadSsfFileProcessor = new LoadSsfFileProcessor("loadSsfFileProcessor", dataProcessorManager,
                aliyunOSSUtil, taskAsyncPool, carVideoRecordService, hitService);
        loadSsfFileProcessor.start();
    }

    /**
     * 获取车辆近30天所有历史视频的时间
     *
     * @param carIds
     * @return
     */
    public List<List<ReplayVideoResponseDTO>> getHistoryVideos(List<String> carIds) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date today = calendar.getTime();
        Date monthAgo = DateUtils.addDays(today, -30);
        List<List<ReplayVideoResponseDTO>> result = new ArrayList<>();
//        for (String carId : carIds) {
//            Criteria criteria = Criteria.where("end_time").gte(monthAgo.getTime()).and("car_id").is(carId);
//            MatchOperation matchOperation = Aggregation.match(criteria);
//            GroupOperation groupOperation = Aggregation.group("car_id", "date").min("start_time").as("start_time").max("end_time").as("end_time");
//            Aggregation aggregation = Aggregation.newAggregation(matchOperation, groupOperation);
//            List<CarVideoRecord> aggregate = carVideoRecordDao.aggregate(aggregation);
//            List<ReplayVideoResponseDTO> replayVideoResponseDTOList = aggregate.stream().map(it -> {
//                ReplayVideoResponseDTO replayVideoResponseDTO = new ReplayVideoResponseDTO();
//                replayVideoResponseDTO.setDate(it.getDate());
//                replayVideoResponseDTO.setStartTime(it.getStartTime() / 1000);
//                replayVideoResponseDTO.setEndTime(it.getEndTime() / 1000);
//                return replayVideoResponseDTO;
//            }).sorted((arg0, arg1) -> arg1.getDate().compareTo(arg0.getDate())).collect(Collectors.toList());
//            result.add(replayVideoResponseDTOList);
//        }

        Criteria criteria = Criteria.where("end_time").gte(monthAgo.getTime()).and("car_id").in(carIds);
        MatchOperation matchOperation = Aggregation.match(criteria);
        GroupOperation groupOperation = Aggregation.group("car_id", "date").min("start_time").as("start_time").max("end_time").as("end_time");
        Aggregation aggregation = Aggregation.newAggregation(matchOperation, groupOperation);
        List<CarVideoRecord> aggregate = carVideoRecordDao.aggregate(aggregation);
        aggregate.stream().collect(Collectors.groupingBy(CarVideoRecord::getCarId)).forEach((carId, list) -> {
            List<ReplayVideoResponseDTO> replayVideoResponseDTOList = list.stream().map(it -> {
                ReplayVideoResponseDTO replayVideoResponseDTO = new ReplayVideoResponseDTO();
                replayVideoResponseDTO.setDate(it.getDate());
                replayVideoResponseDTO.setCarId(carId);
                replayVideoResponseDTO.setStartTime(it.getStartTime() / 1000);
                replayVideoResponseDTO.setEndTime(it.getEndTime() / 1000);
                return replayVideoResponseDTO;
            }).sorted((arg0, arg1) -> arg1.getDate().compareTo(arg0.getDate())).collect(Collectors.toList());
            result.add(replayVideoResponseDTOList);
        });
        // 按照carIds中的顺序排列数据(前端的要求)
        List<List<ReplayVideoResponseDTO>> finalResult = new ArrayList<>();
        for (int i = 0; i < carIds.size(); i++) {
            String carId = carIds.get(i);
            Optional<List<ReplayVideoResponseDTO>> video = result.stream()
                    .filter(list -> list.get(0).getCarId().equals(carId))
                    .findFirst();
            if (video.isEmpty()) continue;
            finalResult.add(video.get());
        }
        return finalResult;
    }

    /**
     * 获取车在当时的位置
     *
     * @param carId     车id
     * @param date      日期
     * @param timestamp 时间戳
     * @return gps坐标
     */
    public GpsLocation getHistoryLocation(String carId, String date, Integer timestamp) {
        GpsLocation gpsLocation = new GpsLocation(0, 0);
        CarVideoRecord record = null;
        try {
            long timestampLong = sdf.parse(date).getTime();
            if (ObjectUtils.isNotEmpty(timestamp)) {
                timestampLong = 1000L * timestamp;
            }
            record = carVideoRecordService.findNextRecord(carId, timestampLong);

        } catch (ParseException e) {
            log.error("parse date met Exception:", e);
        }
        while (record != null && gpsLocation.getLongitude() > 100) {
            OSSObject ossObject = aliyunOSSUtil.getFile(record.getMetaPath());
            try {
                InputStream objectContent = ossObject.getObjectContent();

                //判断是否需要停止播放
                byte[] lengthBytes = objectContent.readNBytes(4);
                if (lengthBytes.length != 4) {
                    return gpsLocation;
                }
                int length = byteArrayToInt(lengthBytes);
                byte[] bytes = objectContent.readNBytes(length);
                if (bytes.length != length) {
                    return gpsLocation;
                }
                FrameMeta.FrameResult frameResult = FrameMeta.FrameResult.parseFrom(bytes);
                //取出FrameResult，放入Replay
                gpsLocation.setLatitude(frameResult.getGpsResult().getLatitude());
                gpsLocation.setLongitude(frameResult.getGpsResult().getLongitude());
            } catch (IOException e) {

            } finally {
                try {
                    ossObject.forcedClose();
                } catch (IOException e) {
                    log.error("close ossObject error:", e);
                }
            }
            record = carVideoRecordService.findNextRecord(carId, record.getEndTime());
        }
        return gpsLocation;
    }

    public void replayVideo(String carId, String uuid, ReplayVideoRequestDTO replayVideoRequestDTO) {
        //相当于给了loadSsfFileProcessor一个播放任务，线程接到任务，run方法不再阻塞
        loadSsfFileProcessor.put(Triple.of(carId, uuid, replayVideoRequestDTO));
    }

    public void stopReplayVideo(String uuid) {
        loadSsfFileProcessor.stopReplay(uuid);
    }

    public void liveVideo(String carId, String sessionId) {
        loadSsfFileProcessor.liveReplay(carId, sessionId);
    }

    public void stopLiveVideo(String carId, String sessionId) {
        loadSsfFileProcessor.stopLiveReplay(carId, sessionId);
    }
}
